using KinonekoSoftware.Studio.Models.Theories;

namespace KinonekoSoftware.Studio.Pages.Galleries.Filters
{
    public sealed class KeywordGalleryFilter : GalleryFilter, IGalleryFilter
    {
        
        //-------------------------------------------------
        //
        //          ReadOnly Fields
        //
        //-------------------------------------------------
        private readonly InstanceGalleryViewModel2           _core;
        private readonly KeywordCatalogRoot                 _root;
        private readonly IRepository                        _repository;
        private readonly KeywordEngine                      _EngineOfKeyword;
        private          Dictionary<string, KeywordCatalog> _map;

        //-------------------------------------------------
        //
        //          Fields
        //
        //-------------------------------------------------

        private KeywordCatalog _catalogRoot;
        private KeywordCatalog _catalog;
        
        //-------------------------------------------------
        //
        //          Constructors
        //
        //-------------------------------------------------
        public KeywordGalleryFilter(KeywordEngine engine, IRepository repository, InstanceGalleryViewModel2 core)
        {
            _EngineOfKeyword = engine     ?? throw new ArgumentNullException(nameof(engine));
            _repository      = repository ?? throw new ArgumentNullException(nameof(repository));
            _core            = core       ?? throw new ArgumentNullException(nameof(core));
            _root            = _repository.GetOrCreate(KeywordCatalogRoot.Create);

            CatalogRootList = new ObservableCollection<KeywordCatalog>();
            CatalogList     = new ObservableCollection<KeywordCatalog>();

            NewKeywordCatalogCommand       = new FuncCommand<KeywordCatalog>(DoNewKeywordCatalogCommand);
            NewKeywordCatalogCommand       = new FuncCommand<KeywordCatalog>(DoNewKeywordCatalogCommand);
            EditKeywordCatalogCommand      = new FuncCommand<KeywordCatalog>(DoEditKeywordCatalogCommand);
            MoveKeywordCatalogCommand      = new FuncCommand<KeywordCatalog>(DoMoveKeywordCatalogCommand);
            ShiftUpKeywordCatalogCommand   = new FuncCommand<KeywordCatalog>(DoShiftUpKeywordCatalogCommand);
            ShiftDownKeywordCatalogCommand = new FuncCommand<KeywordCatalog>(DoShiftDownKeywordCatalogCommand);
            RemoveKeywordCatalogCommand    = new FuncCommand<KeywordCatalog>(DoRemoveKeywordCatalogCommand);
        }
        
        //-------------------------------------------------
        //
        //          Private Methods
        //
        //-------------------------------------------------
        
        private void SaveCatalogRootList()
        {
            _repository.Set(_root);
        }

        //-------------------------------------------------
        //
        //          Private Methods
        //
        //-------------------------------------------------
        private async void DoNewKeywordCatalogCommand(KeywordCatalog parent)
        {
            var r = await NewKeywordCatalogViewModel.New();

            if (!r.IsFinished)
            {
                return;
            }

            var id      = ID.Get();
            var catalog = r.Value;

            if (parent is null) 
            {
                CatalogRootList.Add(catalog);
            }
            else
            {
                catalog.ParentID = parent.Id;
                catalog.Index    = parent.Children.Count;
                parent.Children.Add(catalog);
            }
            
            _map.Add(id, catalog);
            _root.KeywordCatalogs.Add(catalog);
            SaveCatalogRootList();
            
            _core?.ReloadDataSources();
        }


        private async void DoMoveKeywordCatalogCommand(KeywordCatalog element)
        {
            if (element is null)
            {
                return;
            }

            var context = new DialogContext();
            var target = new PseudoCatalog
            {
                Id              = element.Id,
                Children        = new ObservableCollection<PseudoCatalog>(),
                ParentID        = element.ParentID,
                Name            = element.Name,
                IsTargetCatalog = true,
            };


            var rebuild = ArrayRebuild.Rebuild(_root.KeywordCatalogs
                                                    .Select(x => new PseudoCatalog
                                                     {
                                                         Id       = x.Id,
                                                         ParentID = x.ParentID,
                                                         Name     = x.Name,
                                                         Children = new ObservableCollection<PseudoCatalog>(),
                                                     })
                                                    .Where(x => x.Id != element.Id)
                                                    .ToList(), out var pseudoCatalogMap);

            context.Set(MoveCatalogViewModel.PseudoCatalogs, rebuild);
            context.Set(MoveCatalogViewModel.PseudoCatalogMap, pseudoCatalogMap);
            context.Set(MoveCatalogViewModel.TargetElement, target);

            var r = await ViewModelBase.Dialog<PseudoCatalog, MoveCatalogViewModel>(context);

            if (!r.IsFinished)
            {
                return;
            }

            //
            // final done

            if (element.ParentID == target.ParentID && target.Index == element.Index)
            {
                return;
            }


            // remove default catalog
            RemoveOldPosition(element);
            MoveToNewPosition(element, target.ParentID, target.Index);

            //
            // save
            SaveCatalogRootList();
        }

        private void RemoveOldPosition(KeywordCatalog element)
        {
            if (element.IsRoot())
            {
                CatalogRootList.Remove(element);
            }
            else if (_map.TryGetValue(element.ParentID, out var parent))
            {
                parent.Children.Remove(element);
            }

            _root.KeywordCatalogs.Remove(element);
        }

        private void MoveToNewPosition(KeywordCatalog element, string newParentId, int index)
        {
            if (string.IsNullOrEmpty(newParentId))
            {
                CatalogRootList.Insert(index, element);
                for (var i = 0; i < CatalogRootList.Count; i++)
                {
                    CatalogRootList[i].Index = i;
                }
            }
            else if(_map.TryGetValue(newParentId, out var newParent))
            {
                newParent.Children.Insert(index, element);
                    
                for (var i = 0; i < newParent.Children.Count; i++)
                {
                    newParent.Children[i].Index = i;
                }
            }
                
            //
            // set new parent id
            element.ParentID = newParentId;
        }

        private async void DoEditKeywordCatalogCommand(KeywordCatalog element)
        {
            if (element is null)
            {
                return;
            }

            var r   = await ViewModelBase.SingleText("新建文件夹", "输入要创建的文件夹名字");
            var val = r.Value;

            if (!r.IsFinished)
            {
                return;
            }

            element.Name = val;
            _repository.Set(_root);
        }

        private void DoShiftUpKeywordCatalogCommand(KeywordCatalog element)
        {
            if (element is null)
            {
                return;
            }

            if (element.IsRoot())
            {
                CatalogRootList.ShiftUp(element, () =>
                {
                    for (var i = 0; i < CatalogRootList.Count; i++)
                    {
                        CatalogRootList[i].Index          = i;
                    }
                });

            }
            else if (_map.TryGetValue(element.ParentID, out var parent))
            {

                parent.Children.ShiftUp(element, () =>
                {
                    for (var i = 0; i < parent.Children.Count; i++)
                    {
                        parent.Children[i].Index = i;
                    }
                });
            }

            SaveCatalogRootList();
        }

        private void DoShiftDownKeywordCatalogCommand(KeywordCatalog element)
        {

            if (element is null)
            {
                return;
            }

            if (element.IsRoot())
            {
                CatalogRootList.ShiftDown(element, () =>
                {
                    for (var i = 0; i < CatalogRootList.Count; i++)
                    {
                        CatalogRootList[i].Index = i;
                    }
                });

            }
            else if (_map.TryGetValue(element.ParentID, out var parent))
            {

                parent.Children.ShiftDown(element, () =>
                {
                    for (var i = 0; i < parent.Children.Count; i++)
                    {
                        parent.Children[i].Index = i;
                    }
                });
            }

            SaveCatalogRootList();
        }

        #region Remove
        //
        // private void RemoveThis(KeywordCatalog element)
        // {
        //     var queue = new Queue<KeywordCatalog>();
        //     var set   = new HashSet<KeywordCatalog>();
        //     
        //     queue.OnNext(element);
        //
        //     while (queue.Count > 0)
        //     {
        //         var item = queue.Dequeue();
        //
        //         if (string.IsNullOrEmpty(item.ParentID))
        //         {
        //             continue;
        //         }
        //
        //         set.Add(item);
        //     }
        //
        //     foreach (var catalog in set)
        //     {
        //         //
        //         // TODO:
        //         _catalogRoot.CatalogRootList.Remove(catalog);
        //     }
        // }
        
        private async void DoRemoveKeywordCatalogCommand(KeywordCatalog element)
        {
            if (element is null)
            {
                return;
            }
            
            if (!await ViewModelBase.RemoveItem())
            {
                return;
            }

            if (element.IsRoot())
            {
                CatalogRootList.Remove(element);

                if (element.Children is not null &&
                    element.Children.Count > 0)
                {
                    foreach (var child in element.Children)
                    {
                        child.ParentID = null;
                        CatalogRootList.Add(child);
                    }
                }
            }
            else if(_map.TryGetValue(element.ParentID, out var parent))
            {
                if (element.Children is not null &&
                    element.Children.Count > 0)
                {

                    foreach (var child in element.Children)
                    {
                        child.ParentID = parent.Id;
                        parent.Children.Add(child);
                    }

                    parent.Children.Remove(element);
                    element.ParentID = null;

                }
                
            }

            _root.KeywordCatalogs.Remove(element);
            _map.Remove(element.Id);
            SaveCatalogRootList();
        }

        #endregion
        
        //-------------------------------------------------
        //
        //          Public Methods
        //
        //-------------------------------------------------
        public void Initialize()
        {
            var root = ArrayRebuild.Rebuild(_root.KeywordCatalogs, out _map);
            
            //
            //
            CatalogRootList.AddMany(root, true);

            //
            //
            CatalogRoot = CatalogRootList.FirstOrDefault();
        }

        public override List<InstanceCache> Filter(ICollection<InstanceCache> collection, string searchBy)
        {
            IEnumerable<InstanceCache> iterator;
            
            if (CatalogRoot is null || Catalog is null || string.IsNullOrEmpty(Catalog.Name))
            {

                if (string.IsNullOrEmpty(searchBy))
                {
                    iterator = collection;
                }
                else
                {

                    iterator = collection.Where(x => x is not null                 &&
                                                     !string.IsNullOrEmpty(x.Name) &&
                                                     x.Name.Contains(searchBy));

                }
            }
            else
            {
                var documents = _EngineOfKeyword.GetDocuments(Catalog.Name)
                                       .Select(x => x.DocumentID)
                                       .ToHashSet();
                
                if (string.IsNullOrEmpty(searchBy))
                {
                    iterator = collection.Where(x => documents.Contains(x.Id));
                }
                else
                {

                    iterator = collection.Where(x => documents.Contains(x.Id)      &&
                                                     !string.IsNullOrEmpty(x.Name) &&
                                                     x.Name.Contains(searchBy));

                }
            }
            
            
            return iterator.ToList();
        }


        //-------------------------------------------------
        //
        //          Properties
        //
        //-------------------------------------------------
        public override string Name => "标签分类";


        
        public KeywordCatalog Catalog
        {
            get => _catalog;
            set
            {
                SetValue(ref _catalog, value);
                _core?.ReloadDataSources();
            }
        }


        public KeywordCatalog CatalogRoot
        {
            get => _catalogRoot;
            set
            {
                SetValue(ref _catalogRoot, value);
                
                //
                //
                if(_catalogRoot is not null) CatalogList.AddMany(_catalogRoot.Children, true);
            }
        }

        public ObservableCollection<KeywordCatalog> CatalogRootList { get; }
        public ObservableCollection<KeywordCatalog> CatalogList { get; }

        //-------------------------------------------------
        //
        //          Commands
        //
        //-------------------------------------------------

        public ICommandEX NewKeywordCatalogCommand       { get; }
        public ICommandEX EditKeywordCatalogCommand      { get; }
        public ICommandEX MoveKeywordCatalogCommand      { get; }
        public ICommandEX ShiftUpKeywordCatalogCommand   { get; }
        public ICommandEX ShiftDownKeywordCatalogCommand { get; }
        public ICommandEX RemoveKeywordCatalogCommand    { get; }
    }
}